/**
* Copyright (c) By zengqh.
*
* This program is just for fun or demo, in the hope that it  
* will be useful, you can redistribute it and/or modify freely.
*
* Time: 2012/07/16
* File: Math.h
* Blog: http://www.cnblogs.com/zengqh/
**/

#ifndef Math_h__
#define Math_h__

#include "Rfstd.h"

const s32 ROUNDING_ERROR_S32 = 0;
const f32 ROUNDING_ERROR_f32 = 0.000001f;
const f64 ROUNDING_ERROR_f64 = 0.00000001;

const f32 PI				= 3.14159265359f;
const f32 RECIPROCAL_PI		= 1.0f/PI;
const f32 HALF_PI			= PI/2.0f;
const f32 DEGTORAD			= PI / 180.0f;
const f32 RADTODEG			= 180.0f / PI;

const f64 PI64				= 3.1415926535897932384626433832795028841971693993751;
const f64 RECIPROCAL_PI64	= 1.0/PI64;
const f64 DEGTORAD64		= PI64 / 180.0;
const f64 RADTODEG64		= 180.0 / PI64;

inline f32 radToDeg(f32 radians)
{
	return RADTODEG * radians;
}

inline f32 degToRad(f32 degrees)
{
	return DEGTORAD * degrees;
}

template<class T>
inline const T& min_(const T& a, const T& b)
{
	return a < b ? a : b;
}

template<class T>
inline const T& min_(const T& a, const T& b, const T& c)
{
	return a < b ? min_(a, c) : min_(b, c);
}

template<class T>
inline const T& max_(const T& a, const T& b)
{
	return a < b ? b : a;
}

template<class T>
inline const T& max_(const T& a, const T& b, const T& c)
{
	return a < b ? max_(b, c) : max_(a, c);
}

template<class T>
inline T abs_(const T& a)
{
	return a < (T)0 ? -a : a;
}

template<class T>
inline T lerp(const T& a, const T& b, const f32 t)
{
	return (T)(a*(1.f-t)) + (b*t);
}

template <class T>
inline const T clamp (const T& value, const T& low, const T& high)
{
	return min_ (max_(value,low), high);
}

template <class T1, class T2>
inline void swap(T1& a, T2& b)
{
	T1 c(a);
	a = b;
	b = c;
}

inline bool equals(const f64 a, const f64 b, const f64 tolerance = ROUNDING_ERROR_f64)
{
	return (a + tolerance >= b) && (a - tolerance <= b);
}

inline bool equals(const f32 a, const f32 b, const f32 tolerance = ROUNDING_ERROR_f32)
{
	return (a + tolerance >= b) && (a - tolerance <= b);
}

inline bool equals(const s32 a, const s32 b, const s32 tolerance = ROUNDING_ERROR_S32)
{
	return (a + tolerance >= b) && (a - tolerance <= b);
}

//! returns if a equals b, taking an explicit rounding tolerance into account
inline bool equals(const u32 a, const u32 b, const s32 tolerance = ROUNDING_ERROR_S32)
{
	return (a + tolerance >= b) && (a - tolerance <= b);
}


//! returns if a equals zero, taking rounding errors into account
inline bool iszero(const f64 a, const f64 tolerance = ROUNDING_ERROR_f64)
{
	return fabs(a) <= tolerance;
}

//! returns if a equals zero, taking rounding errors into account
inline bool iszero(const f32 a, const f32 tolerance = ROUNDING_ERROR_f32)
{
	return fabsf(a) <= tolerance;
}

//! returns if a equals not zero, taking rounding errors into account
inline bool isnotzero(const f32 a, const f32 tolerance = ROUNDING_ERROR_f32)
{
	return fabsf(a) > tolerance;
}

//! returns if a equals zero, taking rounding errors into account
inline bool iszero(const s32 a, const s32 tolerance = 0)
{
	return ( a & 0x7ffffff ) <= tolerance;
}

//! returns if a equals zero, taking rounding errors into account
inline bool iszero(const u32 a, const u32 tolerance = 0)
{
	return a <= tolerance;
}

inline s32 s32_min(s32 a, s32 b)
{
	const s32 mask = (a - b) >> 31;
	return (a & mask) | (b & ~mask);
}

inline s32 s32_max(s32 a, s32 b)
{
	const s32 mask = (a - b) >> 31;
	return (b & mask) | (a & ~mask);
}

inline s32 s32_clamp (s32 value, s32 low, s32 high)
{
	return s32_min(s32_max(value,low), high);
}


template <typename T>
glm::detail::tvec3<T> getHorizontalAngle(const glm::detail::tvec3<T>& v)
{
	glm::detail::tvec3<T> angle;

	const f64 tmp = (atan2((f64)v.x, (f64)v.z) * RADTODEG64);
	angle.y = (T)tmp;

	if (angle.y < 0)
		angle.y += 360;
	if (angle.y >= 360)
		angle.y -= 360;

	const f64 z1 = sqrt(v.x * v.x + v.z * v.z);

	angle.x = (T)(atan2((f64)z1, (f64)v.y) * RADTODEG64 - 90.0);

	if (angle.x < 0)
		angle.x += 360;
	if (angle.x >= 360)
		angle.x -= 360;

	return angle;
}
#endif    /* Math_h__ */